package com.xli.organ.ou.frameou.service.impl;

import com.alicp.jetcache.Cache;
import com.alicp.jetcache.CacheManager;
import com.alicp.jetcache.anno.CacheConsts;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.template.QuickConfig;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xli.dto.component.util.TreeUtil;
import com.xli.organ.ou.frameou.entity.FrameOu;
import com.xli.organ.ou.frameou.entity.OuCache;
import com.xli.organ.ou.frameou.mapper.FrameOuMapper;
import com.xli.organ.ou.frameou.mapper.IOuCacheMapper;
import com.xli.organ.ou.frameou.service.IFrameOuService;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

@Validated
@Service
public class FrameOuServiceImpl extends ServiceImpl<FrameOuMapper, FrameOu> implements IFrameOuService {

    private final Lock lock = new ReentrantLock();

    @Autowired
    CacheManager cacheManager;

    Cache<Long, List<OuCache>> ouCache;

    @PostConstruct
    public void init() {
        lock.lock();
        try {
            QuickConfig qc = QuickConfig.newBuilder("frame_ou:list:")
                    .expire(Duration.ofSeconds(CacheConsts.DEFAULT_EXPIRE))
                    .cacheType(CacheType.LOCAL)
                    .syncLocal(true)
                    .build();
            ouCache = cacheManager.getOrCreateCache(qc);

            List<FrameOu> frameOuList = this.findList(new QueryWrapper<>());
            List<OuCache> ouCacheList = IOuCacheMapper.INSTANCE.toCacheList(frameOuList);

            Map<Long, List<OuCache>> groupList = ouCacheList.stream()
                    .collect(Collectors.groupingBy(ou -> Optional.ofNullable(ou.getPid()).orElse(TreeUtil.defaultPid)));
            groupList.forEach((pid, ouList) -> {
                ouList.sort(Comparator.comparing(OuCache::getOrder_num, Comparator.nullsLast(Comparator.reverseOrder())));
                ouCache.put(pid, ouList);
            });
        } finally {
            lock.unlock();
        }
    }

    @Override
    public boolean insert(FrameOu frameOu) {
        lock.lock();
        try {
            boolean result = this.save(frameOu);
            if (result) {
                List<OuCache> ouCacheList = ouCache.get(generateKey(frameOu));
                if (ouCacheList == null) {
                    ouCacheList = new ArrayList<>();
                }
                ouCacheList.add(IOuCacheMapper.INSTANCE.toCacheEntity(frameOu));
                ouCacheList.sort(Comparator.comparing(OuCache::getOrder_num, Comparator.nullsLast(Comparator.reverseOrder())));
                ouCache.put(generateKey(frameOu), ouCacheList);
            }
            return result;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public boolean delete(Long id) {
        lock.lock();
        try {
            FrameOu frameOu = this.find(id);
            if (frameOu != null) {
                boolean result = this.removeById(id);
                if (result) {
                    List<OuCache> ouCacheList = ouCache.get(generateKey(frameOu));
                    if (ouCacheList == null) {
                        ouCacheList = new ArrayList<>();
                    }
                    ouCacheList.removeIf(cache -> cache.getId().equals(frameOu.getId()));
                    ouCache.put(generateKey(frameOu), ouCacheList);
                }
                return result;
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public boolean update(FrameOu frameOu) {
        lock.lock();
        try {
            FrameOu ou = this.find(frameOu.getId());
            if (ou != null) {
                boolean result = this.updateById(frameOu);
                if (result) {
                    List<OuCache> ouCacheList = ouCache.get(generateKey(ou));
                    if (ouCacheList == null) {
                        ouCacheList = new ArrayList<>();
                    }
                    ouCacheList.removeIf(cache -> cache.getId().equals(ou.getId()));
                    if (ou.getPid().equals(frameOu.getPid())) {
                        ouCacheList.add(IOuCacheMapper.INSTANCE.toCacheEntity(frameOu));
                        ouCacheList.sort(Comparator.comparing(OuCache::getOrder_num, Comparator.nullsLast(Comparator.reverseOrder())));
                        ouCache.put(generateKey(ou), ouCacheList);
                    } else {
                        ouCache.put(generateKey(ou), ouCacheList);
                        ouCacheList = ouCache.get(generateKey(frameOu));
                        if (ouCacheList == null) {
                            ouCacheList = new ArrayList<>();
                        }
                        ouCacheList.add(IOuCacheMapper.INSTANCE.toCacheEntity(frameOu));
                        ouCacheList.sort(Comparator.comparing(OuCache::getOrder_num, Comparator.nullsLast(Comparator.reverseOrder())));
                        ouCache.put(generateKey(frameOu), ouCacheList);
                    }
                }
                return result;
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public FrameOu find(Long id) {
        return this.getById(id);
    }

    @Override
    public long findCount(QueryWrapper<FrameOu> qw) {
        return this.count(qw);
    }

    @Override
    public List<FrameOu> findList(QueryWrapper<FrameOu> qw) {
        return this.list(qw);
    }

    @Override
    public Page<FrameOu> findList(QueryWrapper<FrameOu> qw, long current, long size) {
        return this.page(new Page<>(current, size), qw);
    }

    @Override
    public List<FrameOu> findAllOuList() {
        return this.findList(new QueryWrapper<>());
    }

    @Override
    public List<FrameOu> findTopOuList() {
        List<OuCache> ouCacheList = ouCache.get(TreeUtil.defaultPid);
        if (ouCacheList == null) {
            ouCacheList = new ArrayList<>();
        }
        return IOuCacheMapper.INSTANCE.toEntityList(ouCacheList, this::find);
    }

    @Override
    public List<FrameOu> findOuListByPid(Long pid) {
        List<OuCache> ouCacheList = ouCache.get(pid);
        if (ouCacheList == null) {
            ouCacheList = new ArrayList<>();
        }
        return IOuCacheMapper.INSTANCE.toEntityList(ouCacheList, this::find);
    }


    private Long generateKey(FrameOu frameOu) {
        if (frameOu != null && frameOu.getPid() != null) {
            return frameOu.getPid();
        }
        return TreeUtil.defaultPid;
    }
}
